//******************************************************************************
//this code is protected by the GNU affero GPLv3
//author:Sylvain BERTRAND <sylvain.bertrand AT gmail dot com>
//                        <digital.ragnarok AT gmail dot com>
//******************************************************************************
#include "globals.h"

static s8 pong(struct client *c,s32 shell_surface_slot,struct msg *m)
{
  s8 r;
  if(m->req_dws<3){
    PERR("client(%d):fatal:shell_surface::frame missing serial\n",c->so);
    r=LWL_ERR;goto exit;
  }

  LOG_WIRE("client(%d):shell_surface::pong serial=%u\n",c->so,m->req[2]);

  r=shell_surface_pong(c,shell_surface_slot,m->req[2]);
exit:
  return r;
}

static s8 move(struct client *c,s32 shell_surface_slot,struct msg *m)
{
  s8 r;
  if(m->req_dws<4){
    PERR("client(%d):fatal:shell_surface::move missing data\n",c->so);
    r=LWL_ERR;goto exit;
  }

  LOG_WIRE("client(%d):shell_surface::move seat_id=%u serial=%u\n",c->so,
                                                           m->req[2],m->req[3]);

  r=shell_surface_move(c,shell_surface_slot,m->req[2],m->req[3]);
exit:
  return r;
}

static s8 resize(struct client *c,s32 shell_surface_slot,struct msg *m)
{
  s8 r;
  if(m->req_dws<5){
    PERR("client(%d):fatal:shell_surface::resize missing data\n",c->so);
    r=LWL_ERR;goto exit;
  }

  LOG_WIRE("client(%d):shell_surface::resize seat_id=%u serial=%u edges=%u\n",
                                           c->so,m->req[2],m->req[3],m->req[4]);

  r=shell_surface_resize(c,shell_surface_slot,m->req[2],m->req[3],m->req[4]);
exit:
  return r;
}

static s8 set_transient(struct client *c,s32 shell_surface_slot,struct msg *m)
{
  s8 r;
  if(m->req_dws<6){
    PERR("client(%d):fatal:shell_surface::set_transient missing data\n",c->so);
    r=LWL_ERR;goto exit;
  }

  LOG_WIRE("client(%d):shell_surface::set_transient parent_surface_id=%u"
                       " x=%d y=%d flags=%u\n",c->so,m->req[2],(s32)m->req[3],
                                                      (s32)m->req[4],m->req[5]);

  r=shell_surface_set_transient(c,shell_surface_slot,m->req[2],(s32)m->req[3],
                                                      (s32)m->req[4],m->req[5]);
exit:
  return r;
}

static s8 set_fullscreen(struct client *c,s32 shell_surface_slot,struct msg *m)
{
  s8 r;
  if(m->req_dws<5){
    PERR("client(%d):fatal:shell_surface::set_fullscreen missing data\n",c->so);
    r=LWL_ERR;goto exit;
  }

  LOG_WIRE("client(%d):shell_surface::set_fullscreen method=%u framerate=%u"
                         " output_id=%u\n",c->so,m->req[2],m->req[3],m->req[4]);

  r=shell_surface_set_full_screen(c,shell_surface_slot,m->req[2],m->req[3],
                                                                     m->req[4]);
exit:
  return r;
}

static s8 set_popup(struct client *c,s32 shell_surface_slot,struct msg *m)
{
  s8 r;
  if(m->req_dws<8){
    PERR("client(%d):fatal:shell_surface::set_popup missing data\n",c->so);
    r=LWL_ERR;goto exit;
  }

  LOG_WIRE("client(%d):shell_surface::set_popup seat_id=%u serial=%u"
                  " parent_surface_id=%u x=%d y=%d flags=%u \n",c->so,m->req[2],
                              m->req[3],m->req[4],(s32)m->req[5],(s32)m->req[6],
                                                                     m->req[7]);

  r=shell_surface_set_popup(c,shell_surface_slot,m->req[2],m->req[3],m->req[4],
                                       (s32)m->req[5],(s32)m->req[6],m->req[7]);
exit:
  return r;
}

static s8 set_maximised(struct client *c,s32 shell_surface_slot,struct msg *m)
{
  s8 r;
  if(m->req_dws<3){
    PERR("client(%d):fatal:shell_surface::set_maximised missing output_id\n",
                                                                         c->so);
    r=LWL_ERR;goto exit;
  }

  LOG_WIRE("client(%d):shell_surface::set_maximised output_id=%u\n",c->so,
                                                                     m->req[2]);

  r=shell_surface_set_maximised(c,shell_surface_slot,m->req[2]);
exit:
  return r;
}

static s8 set_title(struct client *c,s32 shell_surface_slot,struct msg *m)
{
  s8 r;
  if(m->req_dws<3){
    PERR("client(%d):fatal:shell_surface::set_title missing title\n",c->so);
    r=LWL_ERR;goto exit;
  }

  //----------------------------------------------------------------------------
  //parse the title string
  void *title="";
  if(m->req[2]!=0){
    u8 *end=(u8*)&m->req[3];
    while(end<(u8*)(m->req+m->req_dws)){
      if(*end==0) break;
      ++end;
    }

    if(end==(u8*)(m->req+m->req_dws)){
      PERR("client(%d):fatal:shell_surface::set_title title name misses"
                                                         " terminator\n",c->so);
      r=LWL_ERR;goto exit;
    }
    title=&m->req[3];
  }
  //----------------------------------------------------------------------------
 
  LOG_WIRE("client(%d):shell_surface::set_title title=\"%s\"\n",c->so,title);

  r=shell_surface_set_title(c,shell_surface_slot,title);
exit:
  return r;
}

static s8 set_class(struct client *c,s32 shell_surface_slot,struct msg *m)
{
  s8 r;
  if(m->req_dws<3){
    PERR("client(%d):fatal:shell_surface::set_class missing class\n",c->so);
    r=LWL_ERR;goto exit;
  }

  //----------------------------------------------------------------------------
  //parse the class string
  void *class="";
  if(m->req[2]!=0){
    u8 *end=(u8*)&m->req[3];
    while(end<(u8*)(m->req+m->req_dws)){
      if(*end==0) break;
      ++end;
    }

    if(end==(u8*)(m->req+m->req_dws)){
      PERR("client(%d):fatal:shell_surface::set_class class name misses"
                                                         " terminator\n",c->so);
      r=LWL_ERR;goto exit;
    }
    class=&m->req[3];
  }
  //----------------------------------------------------------------------------
 
  LOG_WIRE("client(%d):shell_surface::set_class class=\"%s\"\n",c->so,class);

  r=shell_surface_set_class(c,shell_surface_slot,class);
exit:
  return r;
}

s8 wl_shell_surface_req(struct client *c,s32 shell_surface_slot,struct msg *m)
{
  s8 r;
  switch(m->req_op){
  case WL_SHELL_SURFACE_PONG:
    r=pong(c,shell_surface_slot,m);
    break;
  case WL_SHELL_SURFACE_MOVE:
    r=move(c,shell_surface_slot,m);
    break;
  case WL_SHELL_SURFACE_RESIZE:
    r=resize(c,shell_surface_slot,m);
    break;
  case WL_SHELL_SURFACE_SET_TOP_LEVEL:
    LOG_WIRE("client(%d):shell_surface::set_top_level shell_surface_id=%u\n",
                                                               c->so,m->req[0]);
    r=shell_surface_set_top_level(c,shell_surface_slot);
    break;
  case WL_SHELL_SURFACE_SET_TRANSIENT:
    r=set_transient(c,shell_surface_slot,m);
    break;
  case WL_SHELL_SURFACE_SET_FULLSCREEN:
    r=set_fullscreen(c,shell_surface_slot,m);
    break;
  case WL_SHELL_SURFACE_SET_POPUP:
    r=set_popup(c,shell_surface_slot,m);
    break;
  case WL_SHELL_SURFACE_SET_MAXIMISED:
    r=set_maximised(c,shell_surface_slot,m);
    break;
  case WL_SHELL_SURFACE_SET_TITLE:
    r=set_title(c,shell_surface_slot,m);
    break;
  case WL_SHELL_SURFACE_SET_CLASS:
    r=set_class(c,shell_surface_slot,m);
    break;
  default:
    PERR("client(%d):fatal:shell surface unknown opcode %u\n",c->so,m->req_op);
    r=LWL_ERR;
  };
  return r;
}

#define EV_PING_DWS 3
s8 wl_shell_surface_ev_ping(struct client *c,u32 shell_surface_id,u32 serial)
{
  u32 ev[EV_PING_DWS];

  ev[0]=shell_surface_id;
  u16 ev_sz=EV_PING_DWS*sizeof(u32);
  ev[1]=ev_sz<<16|WL_SHELL_SURFACE_PING;
  ev[2]=serial;

  struct io_vec iov;
  iov.base=&ev;
  iov.len=ev_sz;

  struct msg_hdr msg;
  memset(&msg,0,sizeof(msg));
  msg.iov=&iov;
  msg.iov_len=1;

  LOG_WIRE("client(%d):shell_surface::ping shell_surface_id=%u serial=%u\n",
                                                 c->so,shell_surface_id,serial);

  l r;
  while(1){r=sendmsg(c->so,&msg,0); if(r!=-EINTR) break;}

  if(ISERR(r)){
    PERR("client(%d):fatal(%ld):shell_surface::ping unable to send event\n",
                                                                       c->so,r);
    r=LWL_ERR;goto exit;
  }

  if(r!=ev_sz){
    PERR("client(%d):fatal:shell_surface::ping only %ld/%ld were sent\n",c->so,
                                                                       r,ev_sz);
    r=LWL_ERR;goto exit;
  }

  r=LWL_OK;

exit:
  return (s8)r;
}

#define EV_CONFIGURE_DWS 5
s8 wl_shell_surface_ev_configure(struct client *c,u32 shell_surface_id,
                                                          u32 edges,s32 w,s32 h)
{
  u32 ev[EV_CONFIGURE_DWS];

  ev[0]=shell_surface_id;
  u16 ev_sz=EV_CONFIGURE_DWS*sizeof(u32);
  ev[1]=ev_sz<<16|WL_SHELL_SURFACE_CONFIGURE;
  ev[2]=edges;
  ev[3]=w;
  ev[4]=h;

  struct io_vec iov;
  iov.base=&ev;
  iov.len=ev_sz;

  struct msg_hdr msg;
  memset(&msg,0,sizeof(msg));
  msg.iov=&iov;
  msg.iov_len=1;

  LOG_WIRE("client(%d):shell_surface::configure shell_surface_id=%u edges=%u"
                               " w=%d h=%d\n",c->so,shell_surface_id,edges,w,h);

  l r;
  while(1){r=sendmsg(c->so,&msg,0); if(r!=-EINTR) break;}

  if(ISERR(r)){
    PERR("client(%d):fatal(%ld):shell_surface::configure unable to send"
                                                            " event\n",c->so,r);
    r=LWL_ERR;goto exit;
  }

  if(r!=ev_sz){
    PERR("client(%d):fatal:shell_surface::configure only %ld/%ld were sent\n",
                                                                 c->so,r,ev_sz);
    r=LWL_ERR;goto exit;
  }

  r=LWL_OK;

exit:
  return (s8)r;
}

#define EV_POPUP_DONE_DWS 2
s8 wl_shell_surface_ev_popup_done(struct client *c,u32 shell_surface_id)
{
  u32 ev[EV_POPUP_DONE_DWS];

  ev[0]=shell_surface_id;
  u16 ev_sz=EV_POPUP_DONE_DWS*sizeof(u32);
  ev[1]=ev_sz<<16|WL_SHELL_SURFACE_POPUP_DONE;

  struct io_vec iov;
  iov.base=&ev;
  iov.len=ev_sz;

  struct msg_hdr msg;
  memset(&msg,0,sizeof(msg));
  msg.iov=&iov;
  msg.iov_len=1;

  LOG_WIRE("client(%d):shell_surface::popup_done shell_surface_id=%u\n",c->so,
                                                              shell_surface_id);

  l r;
  while(1){r=sendmsg(c->so,&msg,0); if(r!=-EINTR) break;}

  if(ISERR(r)){
    PERR("client(%d):fatal(%ld):shell_surface::popup_done unable to send"
                                                            " event\n",c->so,r);
    r=LWL_ERR;goto exit;
  }

  if(r!=ev_sz){
    PERR("client(%d):fatal:shell_surface::popup_done only %ld/%ld were sent\n",
                                                                 c->so,r,ev_sz);
    r=LWL_ERR;goto exit;
  }

  r=LWL_OK;

exit:
  return (s8)r;
}
